package com.xiaoshuidi.cloud.module.rooms.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.xiaoshuidi.cloud.framework.common.exception.ServiceException;
import com.xiaoshuidi.cloud.framework.common.pojo.CommonResult;
import com.xiaoshuidi.cloud.framework.common.pojo.PageResult;
import com.xiaoshuidi.cloud.framework.common.util.json.JsonUtils;
import com.xiaoshuidi.cloud.module.rooms.controller.app.map.vo.*;
import com.xiaoshuidi.cloud.module.rooms.enums.*;
import com.xiaoshuidi.cloud.module.rooms.framework.config.ConfigValues;
import com.xiaoshuidi.cloud.module.rooms.framework.utils.BaiDuMapUtil;
import com.xiaoshuidi.cloud.module.rooms.framework.utils.GaoDeUtil;
import com.xiaoshuidi.cloud.module.rooms.framework.utils.RoomHouseUtil;
import com.xiaoshuidi.cloud.module.rooms.pojo.Apartment;
import com.xiaoshuidi.cloud.module.rooms.pojo.RoomHFMetro;
import com.xiaoshuidi.cloud.module.rooms.pojo.RoomHouse;
import com.xiaoshuidi.cloud.module.rooms.pojo.RoomStreetLatlon;
import com.xiaoshuidi.cloud.module.rooms.pojo.roomel.RoomelEntity;
import com.xiaoshuidi.cloud.module.rooms.repository.ApartmentRepository;
import com.xiaoshuidi.cloud.module.rooms.repository.RoomHFMetroRepository;
import com.xiaoshuidi.cloud.module.rooms.repository.RoomHouseRepository;
import com.xiaoshuidi.cloud.module.rooms.repository.RoomStreetLatlonRepository;
import com.xiaoshuidi.cloud.module.rooms.repository.elsticsearch.RoomelEntityRepository;
import com.xiaoshuidi.cloud.module.rooms.service.MapService;
import lombok.NonNull;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.common.geo.GeoPoint;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.BucketOrder;
import org.elasticsearch.search.aggregations.PipelineAggregatorBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.CardinalityAggregationBuilder;
import org.elasticsearch.search.aggregations.metrics.ParsedCardinality;
import org.elasticsearch.search.aggregations.metrics.ParsedTopHits;
import org.elasticsearch.search.aggregations.metrics.TopHitsAggregationBuilder;
import org.elasticsearch.search.aggregations.pipeline.BucketSortPipelineAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.sort.FieldSortBuilder;
import org.elasticsearch.search.sort.SortOrder;
import org.jetbrains.annotations.Nullable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.zxp.esclientrhl.repository.ElasticsearchTemplate;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.stream.Collectors;

import static com.xiaoshuidi.cloud.module.rooms.pojo.roomel.RoomelEntityMapping.*;

/**
 * @author hans.zhang
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MapServiceImpl implements MapService {
    private static final String PREFIXKEY = "rooms:latlon:安徽省:合肥市:";
    private final ElasticsearchTemplate elasticsearchTemplate;
    private final ConfigValues configValues;
    private final RedisTemplate redisTemplate;
    private final RoomStreetLatlonRepository roomStreetLatlonRepository;
    private final ApartmentRepository apartmentRepository;
    private final RoomHouseRepository roomHouseRepository;
    private final GaoDeUtil gaoDeUtil;
    private final RoomHFMetroRepository roomHFMetroRepository;
    private final RoomelEntityRepository roomelEntityRepository;
    private final BaiDuMapUtil baiDuMapUtil;


    /**
     * 根据经纬度获取范围内房源数据
     *
     * @param maxLongitude     最大经度
     * @param minLongitude     最小经度
     * @param maxLatitude      最大维度
     * @param minLatitude      最小经度
     * @param zoom             缩放等级
     * @param tenantId         租户id
     * @param isConcentrated
     * @param district         区域
     * @param monthRentalMin   租金范围-下限
     * @param monthRentalMax   租金范围-上限
     * @param longitude        地铁站经度
     * @param latitude         地铁站纬度
     * @param isWhole          出租方式,是否整租 true:整租 false:合租
     * @param bedRoomNum       户型-几室 1:一室 2:二室 3:三室 4:四室 5:其他
     * @param publicFacilities 特色需求(公共设施)
     * @param roomHouseState   房源状态 1：可租房源 2：全部房源
     * @return
     */
    @Override
    public CommonResult<List<GetMapRespVO>> getMap(Double maxLongitude, Double minLongitude, Double maxLatitude, Double minLatitude, Double zoom, Long tenantId,
                                                   Boolean isConcentrated, String district, Double monthRentalMin, Double monthRentalMax, Double longitude, Double latitude, Boolean isWhole,
                                                   Integer bedRoomNum, String publicFacilities, String roomHouseState) {
        log.info("地图找房参数接收参数：maxLongitude:{},minLongitude:{},maxLatitude:{},minLatitude:{},zoom:{},tenantId:{},isConcentrated:{}" +
                        " district{},monthRentalMin:{},monthRentalMax:{},longitude:{},latitude:{},isWhole:{},bedRoomNum:{},publicFacilities:{},roomHouseState:{}",
                maxLongitude, minLongitude, maxLatitude, minLatitude, zoom, tenantId, isConcentrated, district, monthRentalMin, monthRentalMax, longitude, latitude, isWhole, bedRoomNum, publicFacilities, roomHouseState);
        List<GeoPoint> points = new ArrayList<>();
        points.add(new GeoPoint(maxLatitude, maxLongitude));
        points.add(new GeoPoint(maxLatitude, minLongitude));
        points.add(new GeoPoint(minLatitude, minLongitude));
        points.add(new GeoPoint(minLatitude, maxLongitude));
        // 构建地理位置查询条件
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.geoPolygonQuery("apartmentAreaGeo", points))
                .must(QueryBuilders.termQuery(IsStop, false))
                .must(QueryBuilders.termQuery("tenantId", tenantId));

        // 查询整租 or 合租的子房间
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.should(QueryBuilders.termQuery(IsWhole, true));
        BoolQueryBuilder boolQueryBuilder2 = new BoolQueryBuilder();

        BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
        boolQueryBuilder3.must(QueryBuilders.termQuery(IsWhole, false));
        boolQueryBuilder3.must(QueryBuilders.existsQuery(SuperId));
        boolQueryBuilder2.must(boolQueryBuilder3);

        BoolQueryBuilder boolQueryBuilder5 = new BoolQueryBuilder();
        boolQueryBuilder5.mustNot(QueryBuilders.termQuery(RoomType + ".keyword", RoomTypeEnum.GGQY.getValue()));
        boolQueryBuilder2.must(boolQueryBuilder5);
        boolQueryBuilder.should(boolQueryBuilder2);

        queryBuilder.must(boolQueryBuilder);
        queryBuilder.must(QueryBuilders.termQuery("isLock", false));

        if (ObjectUtil.isNotEmpty(roomHouseState) && roomHouseState.equals("1")) {
            queryBuilder.must(QueryBuilders.termQuery("houseState", RentStatusEnum.EMPTY.getValue()));
        }
        // 构建查询条件
        buildQueryCriteria(isConcentrated, district, monthRentalMin, monthRentalMax, longitude, latitude, isWhole, bedRoomNum, publicFacilities, queryBuilder);
        // 按市分组
        TermsAggregationBuilder aggregationBuilder = AggregationBuilders.terms("cityGroup").field("city.keyword");
        int level;
        //根据地图的缩放比例进行分组
        if (zoom <= 12) { //2公里以上
            // 按区域分组
            TermsAggregationBuilder districtAgg = AggregationBuilders.terms("districtGroup").field("district.keyword");
            // 按房态分组
            TermsAggregationBuilder roomStatusGroup = AggregationBuilders.terms("roomStatusGroup").field("houseState.keyword");
            districtAgg.subAggregation(roomStatusGroup);
            // 将区域分组作为城市分组的子聚合条件
            aggregationBuilder.subAggregation(districtAgg);
            level = 1;
        } else if (zoom < 14) { //200米以上
            // 按区域分组
            TermsAggregationBuilder districtGroup = AggregationBuilders.terms("districtGroup").field("district.keyword");
            // 按街道分组
            TermsAggregationBuilder streetGroup = AggregationBuilders.terms("streetGroup").field("street.keyword");
            TopHitsAggregationBuilder areaInfoGroup = AggregationBuilders.topHits("districtInfo").fetchSource(new String[]{"district"}, null).size(1);
            // 按房态分组
            TermsAggregationBuilder roomStatusGroup = AggregationBuilders.terms("roomStatusGroup").field("houseState.keyword");
            roomStatusGroup.subAggregation(areaInfoGroup);
            streetGroup.subAggregation(roomStatusGroup);
            // 将街道分组作为区域分组的子聚合条件
            districtGroup.subAggregation(streetGroup);
            // 将区域分组作为城市分组的子聚合条件
            aggregationBuilder.subAggregation(districtGroup);
            level = 2;
        } else { //200以下
            // 按区域分组
            TermsAggregationBuilder districtGroup = AggregationBuilders.terms("districtGroup").field("district.keyword");
            // 按街道分组
            TermsAggregationBuilder streetGroup = AggregationBuilders.terms("streetGroup").field("street.keyword");
            // 按小区分组
            TermsAggregationBuilder areaGroup = AggregationBuilders.terms("areaGroup").field("roomAreaId");
            // 获取小区名
            TopHitsAggregationBuilder areaInfoGroup = AggregationBuilders.topHits("areaInfo").fetchSource(new String[]{"apartmentAreaName", "apartmentAreaGeo", "roomAreaId"}, null).size(1);
            // 按房态分组
            TermsAggregationBuilder roomStatusGroup = AggregationBuilders.terms("roomStatusGroup").field("houseState.keyword");
            roomStatusGroup.subAggregation(areaInfoGroup);
            areaGroup.subAggregation(roomStatusGroup);
            // 将小区分组作为街道分组的子聚合条件
            streetGroup.subAggregation(areaGroup);
            // 将街道分组作为区域分组的子聚合条件
            districtGroup.subAggregation(streetGroup);
            // 将区域分组作为城市分组的子聚合条件
            aggregationBuilder.subAggregation(districtGroup);
            level = 3;
        }
        // 原生查询请求
        SearchRequest searchRequest = new SearchRequest(ApiConstants.ROOMELENTIRY);
        // 查询条件构造
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 添加查询条件
        searchSourceBuilder.query(queryBuilder);
        // 添加聚合条件
        searchSourceBuilder.aggregation(aggregationBuilder);
        // 将条件构造作为查询请求的属性
        searchRequest.source(searchSourceBuilder);

        List<GetMapRespVO> respVOS = new ArrayList<>();
        try {
            // 进行查询
            SearchResponse search = elasticsearchTemplate.search(searchRequest);
            log.info("地图找房查询请求：{}", searchRequest.source().toString());
            // 获取市分组下的所有数据
            ParsedTerms parsedTerms = (ParsedTerms) search.getAggregations().asMap().get("cityGroup");
            // 统计到区
            if (level == 1) {
                List<? extends Terms.Bucket> buckets = parsedTerms.getBuckets();
                // 遍历所有组
                buckets.forEach(cityTerm -> {
                    // 获取所有区域分组
                    ParsedTerms districtTerms = (ParsedTerms) cityTerm.getAggregations().getAsMap().get("districtGroup");
                    List<? extends Terms.Bucket> districtTermsBuckets = districtTerms.getBuckets();
                    // 区域分组下的所有数据
                    districtTermsBuckets.forEach(districtTerm -> {
                        // 区域内有几条数据
                        GetMapRespVO respVO = new GetMapRespVO();
                        respVO.setName(districtTerm.getKeyAsString());
                        respVO.setNum(districtTerm.getDocCount());
                        String lngLat = this.getLngLat(respVO.getName());
                        respVO.setLatLon(lngLat);
                        respVO.setLevel(level);
                        // 房态的分组数据
                        ParsedTerms roomStatusGroup = (ParsedTerms) districtTerm.getAggregations().asMap().get("roomStatusGroup");
                        // 过滤出空房的分组
                        Terms.Bucket emptyRoomBucket = roomStatusGroup.getBuckets().stream().filter(p -> RentStatusEnum.EMPTY.getValue().equals(p.getKeyAsString())).findFirst().orElse(null);
                        if (ObjectUtil.isNotEmpty(emptyRoomBucket)) {
                            // 空房的数量
                            respVO.setEmptyRoomNum(emptyRoomBucket.getDocCount());
                        }
                        respVOS.add(respVO);
                    });

                });
            } else if (level == 2) {
                // 统计到街道
                List<? extends Terms.Bucket> buckets = parsedTerms.getBuckets();
                // 遍历所有组
                buckets.forEach(cityTerm -> {
                    // 获取所有区域分组
                    ParsedTerms districtTerms = (ParsedTerms) cityTerm.getAggregations().getAsMap().get("districtGroup");
                    List<? extends Terms.Bucket> districtTermsBuckets = districtTerms.getBuckets();
                    // 区域分组下的所有数据
                    districtTermsBuckets.forEach(districtTerm -> {
                        // 获取所有街道分组
                        ParsedTerms streetTerms = (ParsedTerms) districtTerm.getAggregations().getAsMap().get("streetGroup");
                        List<? extends Terms.Bucket> streetTermsBuckets = streetTerms.getBuckets();
                        // 街道分组下的所有数据
                        streetTermsBuckets.forEach(streetTerm -> {
                            GetMapRespVO respVO = new GetMapRespVO();
                            // 街道下按房态分组
                            ParsedTerms roomStatusGroup = (ParsedTerms) streetTerm.getAggregations().getAsMap().get("roomStatusGroup");
                            // 过滤出空房的分组
                            Terms.Bucket emptyRoomBucket = roomStatusGroup.getBuckets().stream().filter(p -> RentStatusEnum.EMPTY.getValue().equals(p.getKeyAsString())).findFirst().orElse(null);
                            if (ObjectUtil.isNotEmpty(emptyRoomBucket)) {
                                // 空房的数量
                                respVO.setEmptyRoomNum(emptyRoomBucket.getDocCount());
                            }
                            // 只需要找到第一个分组, 拿到街道信息即可
                            ParsedTopHits districtInfo = (ParsedTopHits) roomStatusGroup.getBuckets().get(0).getAggregations().getAsMap().get("districtInfo");
                            for (SearchHit hit : districtInfo.getHits().getHits()) {
                                RoomelEntity roomelEntity = JsonUtils.parseObject(hit.getSourceAsString(), RoomelEntity.class);
                                // 街道内有几条数据
                                respVO.setName(streetTerm.getKeyAsString());
                                respVO.setNum(streetTerm.getDocCount());
                                String lngLat = this.getLngLat(respVO.getName());
                                respVO.setLatLon(lngLat);
                                respVO.setLevel(level);
                                respVO.setDistrict(roomelEntity.getDistrict());
                                respVOS.add(respVO);
                            }
                        });
                    });
                });

            } else {
                // 统计到小区
                List<? extends Terms.Bucket> buckets = parsedTerms.getBuckets();
                // 遍历所有组
                buckets.forEach(cityTerm -> {
                    // 获取所有区域分组
                    ParsedTerms districtTerms = (ParsedTerms) cityTerm.getAggregations().getAsMap().get("districtGroup");
                    List<? extends Terms.Bucket> districtTermsBuckets = districtTerms.getBuckets();
                    // 区域分组下的所有数据
                    districtTermsBuckets.forEach(districtTerm -> {
                        // 获取所有街道分组
                        ParsedTerms streetTerms = (ParsedTerms) districtTerm.getAggregations().getAsMap().get("streetGroup");
                        List<? extends Terms.Bucket> streetTermsBuckets = streetTerms.getBuckets();
                        // 街道分组下的所有数据
                        streetTermsBuckets.forEach(streetTerm -> {
                            // 获取所有小区分组
                            ParsedTerms areaTerms = (ParsedTerms) streetTerm.getAggregations().getAsMap().get("areaGroup");
                            List<? extends Terms.Bucket> areaTermsBuckets = areaTerms.getBuckets();
                            areaTermsBuckets.forEach(areaTerm -> {
                                // 小区内有几条数据
                                GetMapRespVO respVO = new GetMapRespVO();
                                respVO.setRoomAreaId(Long.parseLong(areaTerm.getKeyAsString()));
                                respVO.setNum(areaTerm.getDocCount());
                                ParsedTerms roomStatusGroup = (ParsedTerms) areaTerm.getAggregations().getAsMap().get("roomStatusGroup");

                                // 过滤出空房的分组
                                Terms.Bucket emptyRoomBucket = roomStatusGroup.getBuckets().stream().filter(p -> RentStatusEnum.EMPTY.getValue().equals(p.getKeyAsString())).findFirst().orElse(null);
                                if (ObjectUtil.isNotEmpty(emptyRoomBucket)) {
                                    // 空房的数量
                                    respVO.setEmptyRoomNum(emptyRoomBucket.getDocCount());
                                }

                                // 只需要找到第一个分组, 拿到小区信息即可
                                ParsedTopHits areaInfo = (ParsedTopHits) roomStatusGroup.getBuckets().get(0).getAggregations().getAsMap().get("areaInfo");
                                SearchHit[] hits = areaInfo.getHits().getHits();
                                // 检查小区内是否含有集中式房源
                                if (ObjectUtil.isNotEmpty(hits)) {
                                    respVO.setIsHasConcentrated(checkIsPresent(respVO.getRoomAreaId(), respVO));
                                }
                                for (SearchHit hit : hits) {
                                    Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                                    String apartmentAreaName = (String) sourceAsMap.get("apartmentAreaName");
                                    String geo = (String) sourceAsMap.get("apartmentAreaGeo");
                                    String[] geoSplit = geo.split(",");
                                    if (ObjectUtil.isNotEmpty(geoSplit)) {
                                        respVO.setLatLon(String.valueOf(geoSplit[1] + "," + geoSplit[0]));
                                    }
                                    respVO.setName(apartmentAreaName);
                                }
                                respVO.setLevel(level);
                                respVOS.add(respVO);
                            });
                        });
                    });
                });
            }
        } catch (Exception e) {
            log.error("MapServiceImpl -> getMap异常:", e);
            throw new RuntimeException(e);
        }

        return CommonResult.success(respVOS);
    }

    private Boolean checkIsPresent(Long roomAreaId, GetMapRespVO respVO) {
        List<RoomHouse> roomHouses = roomHouseRepository.getByRoomAreaIdAndIsConcentrated(roomAreaId, true);
        if (ObjectUtil.isNotEmpty(roomHouses)) {
            // 从业务方面, 一个小区只会对应一个门店, 因此取一个房源的门店ID作为该小区的门店ID
            respVO.setApartmentId(roomHouses.get(0).getApartmentId());
        }
        return ObjectUtil.isNotEmpty(roomHouses);
    }

    private static void buildQueryCriteria(Boolean isConcentrated, String district, Double monthRentalMin, Double monthRentalMax, Double longitude, Double latitude, Boolean isWhole, Integer bedRoomNum, String publicFacilities, BoolQueryBuilder boolQueryBuilder) {
        // 区域
        if (ObjectUtil.isNotEmpty(isConcentrated)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("isConcentrated", isConcentrated));
        }
        // 区域
        if (ObjectUtil.isNotEmpty(district)) {
            boolQueryBuilder.must(QueryBuilders.wildcardQuery("district", "*" + district + "*"));
        }

        // 价格区间
        if (ObjectUtil.isAllNotEmpty(monthRentalMin, monthRentalMax)) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(monthRentalMin).lte(monthRentalMax));
        } else if (ObjectUtil.isNotEmpty(monthRentalMin) && ObjectUtil.isEmpty(monthRentalMax)) {
            boolQueryBuilder.must(QueryBuilders.rangeQuery("price").gte(monthRentalMin));
        }

        // 地理位置
        if (ObjectUtil.isAllNotEmpty(longitude, latitude)) {
            boolQueryBuilder.must(QueryBuilders.geoDistanceQuery("apartmentGeo").distance("2000").point(latitude, longitude));
        }

        // 出租方式
        if (ObjectUtil.isNotEmpty(isWhole)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("isWhole", isWhole));
        }

        // 几室
        if (ObjectUtil.isNotEmpty(bedRoomNum)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("bedroomNum", bedRoomNum));
        }

        // 特色需求
        if (ObjectUtil.isNotEmpty(publicFacilities)) {
            String[] split = publicFacilities.split(",");
            BoolQueryBuilder shouldBoolQueryBuilder = QueryBuilders.boolQuery();
            for (String s : split) {
                shouldBoolQueryBuilder.should(QueryBuilders.matchQuery("publicFacilities", s));
            }
            boolQueryBuilder.must(shouldBoolQueryBuilder);
        }
    }

    @Nullable
    private String getLngLat(String name) {
        // 区域的经纬度需要从redis中获取
        String key = getKey(name);
        String lngLat = (String) redisTemplate.opsForValue().get(key);
        if (StrUtil.isEmpty(lngLat)) {
            // redis中没有经纬度,则需要从百度获取
            log.info("百度API-地址转经纬度, 请求地址:{}", name);
            String baiduAddressToLonLatUrl = configValues.getBaiduAddressToLonLatUrl();
            baiduAddressToLonLatUrl = StrUtil.format(baiduAddressToLonLatUrl, name, configValues.getBaiduAK());
            // 指定上级城市
            baiduAddressToLonLatUrl = baiduAddressToLonLatUrl + "&city=合肥市";
            String resp = HttpUtil.get(baiduAddressToLonLatUrl);
            log.info("百度API-地址转经纬度, 请求地址:{}, 百度响应:{}", name, resp);
            JSONObject respJsonObj = JSONObject.parseObject(resp);
            if (!respJsonObj.get("status").toString().equals("0")) {
                return null;
            }
            JSONObject resultObj = (JSONObject) respJsonObj.get("result");
            JSONObject locationObj = (JSONObject) resultObj.get("location");
            String lng = locationObj.get("lng").toString();
            String lat = locationObj.get("lat").toString();
            log.info("百度API-地址转经纬度, 地址:{}, 经纬度:{}", name, lng + "," + lat);
            lngLat = lng + "," + lat;
        }
        return lngLat;
    }

    private String getKey(String name) {
        StringBuilder sb = new StringBuilder(PREFIXKEY);
        sb.append(name);
        return sb.toString();
    }


    // 同步所有街道经纬度到redis
    public CommonResult<Boolean> snycLatLonToRedis() {
        List<RoomStreetLatlon> list = roomStreetLatlonRepository.list();
        list.forEach(roomStreetLatlon -> {
            String key = this.getKey(roomStreetLatlon.getName());
            redisTemplate.opsForValue().set(key, roomStreetLatlon.getLon() + "," + roomStreetLatlon.getLat());
        });
        return CommonResult.success(Boolean.TRUE);
    }

    /**
     * 通勤时间找房
     *
     * @param lat
     * @param lng
     * @param minute   时间（分钟）
     * @param mode     通勤方式
     * @param viewMode 查看模式 1：公寓 2：（合租）房源
     * @param tenantId
     * @return
     */
    @Override
    public CommonResult<PageResult<CommutingRimeFindRoomRespVO>> commutingtimefindroom(String address, Double lat, Double lng, Integer minute, String mode, Integer viewMode, Integer pageNum, Integer pageSize, Long tenantId) {
        List<CommutingRimeFindRoomRespVO> respVOS = new ArrayList<>();
        boolean flag = true;
        Double latitude = null;
        Double longitude = null;
        if(ObjectUtil.isAllNotEmpty(lat, lng)){
            latitude = lat;
            longitude = lng;
        } else if (ObjectUtil.isNotEmpty(address)) {
            // 根据地址获取经纬度
            String location = baiDuMapUtil.addressToLonLat(address);
            if (ObjectUtil.isEmpty(location)) {
                return CommonResult.success(new PageResult<>(respVOS, 0l));
            } else {
                String[] split = location.split(",");
                latitude = Double.parseDouble(split[1]);
                longitude = Double.parseDouble(split[0]);
            }
        }else {
            // 地址不能为空
            flag = false;
        }

        // 根据时间和通勤方式计算大概的距离
        Integer speed = CommutingMode.getSpeedByValue(mode);
        BigDecimal distance = BigDecimal.valueOf(speed).divide(BigDecimal.valueOf(60), 2, RoundingMode.HALF_UP).multiply(BigDecimal.valueOf(minute));
        // ES查询目标地点范围内的门店/房源
        // 原生查询请求
        SearchRequest searchRequest = new SearchRequest(ApiConstants.ROOMELENTIRY);
        // 查询条件构造
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 判断查询公寓/房源
        String distanceQueryName = ViewModeEnum.GY.getValue().equals(viewMode) ? "apartmentGeo" : "apartmentAreaGeo";
        // 只查询superId为空的房源信息
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery(IsStop, false))
                .must(QueryBuilders.matchQuery(TenantId, tenantId));
        // 构建地理范围查询
        if (flag) {
            queryBuilder.must(QueryBuilders.geoDistanceQuery(distanceQueryName).distance(distance.toString()).point(latitude, longitude));
        }
        if (ViewModeEnum.GY.getValue().equals(viewMode)) {
            queryBuilder.must(QueryBuilders.termQuery("isConcentrated", true));
            TermsAggregationBuilder apartmentGroupAgg = AggregationBuilders.terms("apartmentGroup").field("apartmentId").size(10000);
            TopHitsAggregationBuilder apartmentInfoTopHits = AggregationBuilders.topHits("apartmentInfo")
                    .fetchSource(new String[]{"apartmentId", "apartmentName", "price"}, null).sort("price", SortOrder.ASC).size(1);
            // 桶排序，并对桶分页
            List<FieldSortBuilder> sortList = new ArrayList<>();
            BucketSortPipelineAggregationBuilder bucketSort = PipelineAggregatorBuilders.bucketSort("testSort", sortList).from((pageNum - 1) * pageSize).size(pageSize);
            apartmentGroupAgg.subAggregation(bucketSort);
            apartmentGroupAgg.subAggregation(apartmentInfoTopHits);
            // 获取分组后的桶数量
            CardinalityAggregationBuilder cardinalityAggregationBuilder = AggregationBuilders.cardinality("bucketCount").field("apartmentId");
            searchSourceBuilder.aggregation(apartmentGroupAgg);
            searchSourceBuilder.aggregation(cardinalityAggregationBuilder);
            searchSourceBuilder.size(0);

        } else {
            queryBuilder.must(QueryBuilders.termQuery("isConcentrated", false))
                    .mustNot(QueryBuilders.termQuery("roomType.keyword", RoomTypeEnum.GGQY.getValue()))
                    .must(QueryBuilders.existsQuery("price"));
            // 分散式只查询整租 or 合租的子房间
            BoolQueryBuilder boolQueryBuilderA = new BoolQueryBuilder();
            boolQueryBuilderA.should(QueryBuilders.termQuery("isWhole", true));
            BoolQueryBuilder boolQueryBuilder2 = new BoolQueryBuilder();

            BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
            boolQueryBuilder3.must(QueryBuilders.termQuery("isWhole", false));
            boolQueryBuilder3.must(QueryBuilders.existsQuery("superId"));
            boolQueryBuilder2.must(boolQueryBuilder3);
            boolQueryBuilderA.should(boolQueryBuilder2);

            queryBuilder.must(boolQueryBuilderA);
            searchSourceBuilder.from((pageNum - 1) * pageSize);
            searchSourceBuilder.size(pageNum * pageSize);
        }
        searchSourceBuilder.query(queryBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = elasticsearchTemplate.search(searchRequest);
            log.info("通勤找房ES查询语句：{}", searchRequest.source().toString());
        } catch (Exception e) {
            log.error("MapServiceImpl -> commutingtimefindroom异常:", e);
            throw new RuntimeException(e);
        }

        long count = 0;
        if (ViewModeEnum.GY.getValue().equals(viewMode)) {
            ParsedCardinality cardinality = (ParsedCardinality) searchResponse.getAggregations().getAsMap().get("bucketCount");
            count = cardinality.getValue();
            ParsedTerms apartmentGroupTerms = (ParsedTerms) searchResponse.getAggregations().getAsMap().get("apartmentGroup");
            Double finalLongitude = longitude;
            Double finalLatitude = latitude;
            apartmentGroupTerms.getBuckets().forEach(apartmentGroupTermBucket -> {
                ParsedTopHits apartmentGroupTopHits = (ParsedTopHits) apartmentGroupTermBucket.getAggregations().getAsMap().get("apartmentInfo");
                for (SearchHit hit : apartmentGroupTopHits.getHits().getHits()) {
                    RoomelEntity roomelEntity = JsonUtils.parseObject(hit.getSourceAsString(), RoomelEntity.class);
                    Long apartmentId = roomelEntity.getApartmentId();
                    Apartment apartment = apartmentRepository.getById(apartmentId);
                    if (ObjectUtil.isEmpty(apartment)) {
                        break;
                    }
                    Double meter = null;
                    Double time = null;
                    if (ObjectUtil.isAllNotEmpty(finalLongitude, finalLatitude)) {
                        // 查询需要多久才能到,需要计算两点之间的距离
                        String origins = finalLongitude + "," + finalLatitude;
                        String destination = apartment.getLongitude() + "," + apartment.getLatitude();
                        time = baiDuMapUtil.walkingRoutePlanning(origins, destination, mode, BaiDuMapUtil.DURATION);
//                        time = new BigDecimal(meter).divide(new BigDecimal(speed), 3, RoundingMode.HALF_UP).multiply(new BigDecimal(60)).setScale(0, RoundingMode.CEILING);
                    }

                    String picUrl = apartment.getPicUrl();
                    if (StrUtil.isNotEmpty(picUrl)) {
                        picUrl = picUrl.split(",")[0];
                    }
                    CommutingRimeFindRoomRespVO respVO = CommutingRimeFindRoomRespVO.builder()
                            .id(apartmentId)
                            .name(apartment.getName())
                            .address(apartment.getAddress())
                            .storeTel(apartment.getStoreTel())
                            .monthRental(roomelEntity.getPrice())
                            .time(ObjectUtil.isNotEmpty(time) ? time.toString() : null)
                            .distance(meter)
                            .picUrl(picUrl).build();
                    respVOS.add(respVO);
                }
            });
        } else {
            SearchHits searchHits = searchResponse.getHits();
            count = searchHits.getTotalHits().value;
            for (SearchHit hit : searchHits.getHits()) {
                // 查看房源信息
                Long roomHouseId = Long.valueOf(hit.getSourceAsMap().get("id").toString());
                // lat + , + lon
                String geo = hit.getSourceAsMap().get("apartmentAreaGeo").toString();
                Long apartmentId = Long.valueOf((Integer) hit.getSourceAsMap().get("apartmentId"));
                Apartment apartment = apartmentRepository.getById(apartmentId);
                if (ObjectUtil.isEmpty(apartment)) {
                    break;
                }
                String houseAddress = (String) hit.getSourceAsMap().get("address");
                // 查询最低房价
                List<RoomHouse> roomHouses = roomHouseRepository.getByIdAndSuperIdOrderByMonthRental(roomHouseId);
                // 最低月租金
                Double monthRental = null;
                if (ObjectUtil.isNotEmpty(roomHouses)) {
                    monthRental = roomHouses.get(0).getMonthRental();
                }
                Double meter = null;
                Double time = null;
                if (ObjectUtil.isAllNotEmpty(longitude, latitude)) {
                    // 查询需要多久才能到,需要计算两点之间的距离
                    // 出发点经纬度
                    String origins = longitude + "," + latitude;
                    // 目的地经纬度
                    String[] geoSplit = geo.split(",");
                    String destination = "";
                    if (ObjectUtil.isNotEmpty(geoSplit)) {
                        destination = geoSplit[1] + "," + geoSplit[0];
                    }
                    time = baiDuMapUtil.walkingRoutePlanning(origins, destination, mode, BaiDuMapUtil.DURATION);
//                    time = new BigDecimal(meter).divide(new BigDecimal(speed), 3, RoundingMode.HALF_UP).multiply(new BigDecimal(60)).setScale(0, RoundingMode.CEILING);
                }

                RoomHouse roomHouse = roomHouseRepository.myGetById(roomHouseId);
                String picUrl = roomHouse.getPicUrl();
                if (StrUtil.isNotEmpty(picUrl)) {
                    picUrl = picUrl.split(",")[0];
                }
                CommutingRimeFindRoomRespVO respVO = CommutingRimeFindRoomRespVO.builder()
                        .id(roomHouseId)
                        .name(RoomHouseUtil.getHouseFullName(roomHouse))
                        .address(houseAddress)
                        .storeTel(apartment.getStoreTel())
                        .monthRental(monthRental)
                        .time(ObjectUtil.isNotEmpty(time) ? time.toString() : null)
                        .distance(meter)
                        .picUrl(picUrl).build();
                respVOS.add(respVO);
            }
        }
        return CommonResult.success(new PageResult<>(respVOS, count));
    }

    /**
     * 通勤距离找房
     *
     * @param lat
     * @param lng
     * @param distance 距离（公里）
     * @param viewMode 查看模式： 1：公寓（门店） 2：（房源）房源
     * @param pageNum  第几页
     * @param pageSize 每页大小
     * @param tenantId
     * @return
     */
    @Override
    public CommonResult<PageResult<CommutingDistanceFindRoomRespVO>> commutingDistanceFindRoom(String address, Double lat, Double lng, Integer distance, Integer viewMode, Integer pageNum, Integer pageSize, Long tenantId) {
        List<CommutingDistanceFindRoomRespVO> respVOS = new ArrayList<>();
        boolean flag = true;
        Double latitude = null;
        Double longitude = null;
        if(ObjectUtil.isAllNotEmpty(lat, lng)){
            latitude = lat;
            longitude = lng;
        } else if (ObjectUtil.isNotEmpty(address)) {
            // 根据地址获取经纬度
            String location = baiDuMapUtil.addressToLonLat(address);
            if (ObjectUtil.isEmpty(location)) {
                return CommonResult.success(new PageResult<>(respVOS, 0l));
            } else {
                String[] split = location.split(",");
                latitude = Double.parseDouble(split[1]);
                longitude = Double.parseDouble(split[0]);
            }
        }else {
            // 地址不能为空
            flag = false;
        }

        // ES查询目标地点范围内的门店/房源
        // 原生查询请求
        SearchRequest searchRequest = new SearchRequest(ApiConstants.ROOMELENTIRY);
        // 查询条件构造
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 判断查询公寓/房源
        String distanceQueryName = ViewModeEnum.GY.getValue().equals(viewMode) ? "apartmentGeo" : "apartmentAreaGeo";
        // 只查询superId为空的房源信息
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery(IsStop, false))
                .must(QueryBuilders.termQuery(TenantId, tenantId));
        // 如果录入了地址，且地址能查询到经纬度，则构建地理范围查询
        if (flag) {
            queryBuilder.must(QueryBuilders.geoDistanceQuery(distanceQueryName).distance(distance.toString()).point(latitude, longitude));
        }
        if (ViewModeEnum.GY.getValue().equals(viewMode)) {
            queryBuilder.must(QueryBuilders.termQuery("isConcentrated", true));
            TermsAggregationBuilder apartmentGroupAgg = AggregationBuilders.terms("apartmentGroup")
                    .field("apartmentId").order(BucketOrder.key(true)).size(10000);
            TopHitsAggregationBuilder apartmentInfoTopHits = AggregationBuilders.topHits("apartmentInfo")
                    .fetchSource(new String[]{"apartmentId", "apartmentName", "price"}, null).sort("price", SortOrder.ASC).size(1);
            // 桶排序，并对桶分页
            List<FieldSortBuilder> sortList = new ArrayList<>();
            BucketSortPipelineAggregationBuilder bucketSort = PipelineAggregatorBuilders.bucketSort("testSort", sortList).from((pageNum - 1) * pageSize).size(pageSize);
            apartmentGroupAgg.subAggregation(bucketSort);
            apartmentGroupAgg.subAggregation(apartmentInfoTopHits);
            // 获取分组后的桶数量
            CardinalityAggregationBuilder cardinalityAggregationBuilder = AggregationBuilders.cardinality("bucketCount").field("apartmentId");
            searchSourceBuilder.aggregation(apartmentGroupAgg);
            searchSourceBuilder.aggregation(cardinalityAggregationBuilder);
            searchSourceBuilder.size(0);
        } else {

            queryBuilder.must(QueryBuilders.termQuery("isConcentrated", false))
                    .mustNot(QueryBuilders.termQuery("roomType.keyword", RoomTypeEnum.GGQY.getValue()))
                    .must(QueryBuilders.existsQuery("price"));
            // 分散式只查询整租 or 合租的子房间
            BoolQueryBuilder boolQueryBuilderA = new BoolQueryBuilder();
            boolQueryBuilderA.should(QueryBuilders.termQuery("isWhole", true));
            BoolQueryBuilder boolQueryBuilder2 = new BoolQueryBuilder();

            BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
            boolQueryBuilder3.must(QueryBuilders.termQuery("isWhole", false));
            boolQueryBuilder3.must(QueryBuilders.existsQuery("superId"));
            boolQueryBuilder2.must(boolQueryBuilder3);
            boolQueryBuilderA.should(boolQueryBuilder2);

            queryBuilder.must(boolQueryBuilderA);
            searchSourceBuilder.from((pageNum - 1) * pageSize);
            searchSourceBuilder.size(pageNum * pageSize);
        }
        searchSourceBuilder.query(queryBuilder);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        try {
            searchResponse = elasticsearchTemplate.search(searchRequest);
        } catch (Exception e) {
            log.error("MapServiceImpl -> commutingDistanceFindRoom异常:", e);
            throw new RuntimeException(e);
        }
        long count = 0;
        if (ViewModeEnum.GY.getValue().equals(viewMode)) {
            ParsedCardinality parsedCardinality = (ParsedCardinality) searchResponse.getAggregations().getAsMap().get("bucketCount");
            count = parsedCardinality.getValue();
            ParsedTerms apartmentGroupTerms = (ParsedTerms) searchResponse.getAggregations().getAsMap().get("apartmentGroup");
            Double finalLongitude = longitude;
            Double finalLatitude = latitude;
            apartmentGroupTerms.getBuckets().forEach(apartmentGroupTermBucket -> {
                ParsedTopHits apartmentGroupTopHits = (ParsedTopHits) apartmentGroupTermBucket.getAggregations().getAsMap().get("apartmentInfo");
                for (SearchHit hit : apartmentGroupTopHits.getHits().getHits()) {
                    RoomelEntity roomelEntity = JsonUtils.parseObject(hit.getSourceAsString(), RoomelEntity.class);
                    Apartment apartment = apartmentRepository.getById(roomelEntity.getApartmentId());
                    if (apartment == null) {
                        continue;
                    }
                    Double meter = null;
                    if (ObjectUtil.isAllNotEmpty(finalLongitude, finalLatitude)) {
                        // 查询需要多久才能到,需要计算两点之间的距离
                        String origins = finalLongitude + "," + finalLatitude;
                        String destination = apartment.getLongitude() + "," + apartment.getLatitude();
                        meter = baiDuMapUtil.walkingRoutePlanning(origins, destination, BaiDuMapUtil.WALKING, BaiDuMapUtil.DISTANCE);
                    }

                    String picUrl = apartment.getPicUrl();
                    if (StrUtil.isNotEmpty(picUrl)) {
                        picUrl = picUrl.split(",")[0];
                    }
                    CommutingDistanceFindRoomRespVO respVO = CommutingDistanceFindRoomRespVO.builder()
                            .id(roomelEntity.getApartmentId())
                            .name(apartment.getName())
                            .address(apartment.getAddress())
                            .storeTel(apartment.getStoreTel())
                            .monthRental(roomelEntity.getPrice())
                            .distance(ObjectUtil.isNotEmpty(meter) ? meter.toString() : null)
                            .picUrl(picUrl).build();
                    respVOS.add(respVO);
                }
            });
        } else {
            SearchHits searchHits = searchResponse.getHits();
            SearchHit[] hits = searchHits.getHits();
            count = searchHits.getTotalHits().value;
            for (SearchHit hit : hits) {
                // 查看房源信息
                RoomelEntity roomelEntity = JsonUtils.parseObject(hit.getSourceAsString(), RoomelEntity.class);
                Long roomHouseId = Long.valueOf(hit.getSourceAsMap().get("id").toString());
                // lat + , + lon
                String geo = hit.getSourceAsMap().get("apartmentAreaGeo").toString();
                Long apartmentId = Long.valueOf((Integer) hit.getSourceAsMap().get("apartmentId"));
                Apartment apartment = apartmentRepository.getById(apartmentId);
                if (apartment == null) {
                    continue;
                }
                Double meter = null;
                if (ObjectUtil.isAllNotEmpty(longitude, latitude)) {
                    // 查询需要多久才能到,需要计算两点之间的距离
                    // 出发点经纬度
                    String origins = longitude + "," + latitude;
                    // 目的地经纬度
                    String[] geoSplit = geo.split(",");
                    String destination = "";
                    if (ObjectUtil.isNotEmpty(geoSplit)) {
                        destination = geoSplit[1] + "," + geoSplit[0];
                    }
                    meter = baiDuMapUtil.walkingRoutePlanning(origins, destination, BaiDuMapUtil.WALKING, BaiDuMapUtil.DISTANCE);
                }

                RoomHouse roomHouse = roomHouseRepository.myGetById(roomHouseId);
                String picUrl = roomHouse.getPicUrl();
                if (StrUtil.isNotEmpty(picUrl)) {
                    picUrl = picUrl.split(",")[0];
                }
                CommutingDistanceFindRoomRespVO respVO = CommutingDistanceFindRoomRespVO.builder()
                        .id(roomHouseId)
                        .name(RoomHouseUtil.getHouseFullName(roomHouse))
                        .address(roomHouse.getAddress())
                        .storeTel(apartment.getStoreTel())
                        .monthRental(roomelEntity.getPrice())
                        .distance(ObjectUtil.isNotEmpty(meter) ? meter.toString() : null)
                        .picUrl(picUrl).build();
                respVOS.add(respVO);
            }
        }
        return CommonResult.success(new PageResult<>(respVOS, count));
    }

    /**
     * 地图找房-查看小区房源情况
     *
     * @param roomAreaId 小区id
     * @param tenantId
     * @param pageNo
     * @param pageSize
     * @return
     */
    @Override
    public CommonResult<GetApartmentAreaInfoRespVO> getApartmentAreaInfo(@NonNull Long roomAreaId, Long tenantId, Integer pageNo, Integer pageSize) {
        GetApartmentAreaInfoRespVO getApartmentAreaInfoRespVO = new GetApartmentAreaInfoRespVO();
        // 查询小区下所有的房源
        SearchRequest searchRequest = new SearchRequest(ApiConstants.ROOMELENTIRY);
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        BoolQueryBuilder queryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.termQuery(TenantId, tenantId))
                .must(QueryBuilders.termQuery(IsStop, false))
                .must(QueryBuilders.termQuery(RoomAreaId, roomAreaId));

        // 查询整租 or 合租的子房间
        BoolQueryBuilder boolQueryBuilder = new BoolQueryBuilder();
        boolQueryBuilder.should(QueryBuilders.termQuery(IsWhole, true));
        BoolQueryBuilder boolQueryBuilder2 = new BoolQueryBuilder();

        BoolQueryBuilder boolQueryBuilder3 = new BoolQueryBuilder();
        boolQueryBuilder3.must(QueryBuilders.termQuery(IsWhole, false));
        boolQueryBuilder3.must(QueryBuilders.existsQuery(SuperId));
        boolQueryBuilder2.must(boolQueryBuilder3);

        BoolQueryBuilder boolQueryBuilder5 = new BoolQueryBuilder();
        boolQueryBuilder5.mustNot(QueryBuilders.termQuery(RoomType + ".keyword", RoomTypeEnum.GGQY.getValue()));
        boolQueryBuilder2.must(boolQueryBuilder5);
        boolQueryBuilder.should(boolQueryBuilder2);

        queryBuilder.must(boolQueryBuilder);

        searchSourceBuilder.query(queryBuilder);
        searchSourceBuilder.from((pageNo - 1) * pageSize);
        searchSourceBuilder.size(pageSize);
        searchRequest.source(searchSourceBuilder);
        SearchResponse searchResponse = null;
        Long emptyRoomNum = new Long(0);
        try {
            // 分页查询
            searchResponse = elasticsearchTemplate.search(searchRequest);
            // 查询该小区下的空房数量
            queryBuilder.must(QueryBuilders.termQuery("isLock", false));
            queryBuilder.must(QueryBuilders.termQuery("houseState", RentStatusEnum.EMPTY.getValue()));
            SearchSourceBuilder searchBuilder = new SearchSourceBuilder();
            searchBuilder.query(queryBuilder);
            searchRequest.source(searchBuilder);
            SearchResponse emptySearchResponse = elasticsearchTemplate.search(searchRequest);
            emptyRoomNum = emptySearchResponse.getHits().getTotalHits().value;
        } catch (Exception e) {
            log.error("MapServiceImpl -> getApartmentAreaInfo异常:", e);
            throw new RuntimeException(e);
        }
        long total = searchResponse.getHits().getTotalHits().value;
        SearchHit[] hits = searchResponse.getHits().getHits();
        SearchHit searchHit = Arrays.stream(searchResponse.getHits().getHits()).filter(p -> {
            RoomelEntity roomelEntity = JsonUtils.parseObject(p.getSourceAsString(), RoomelEntity.class);
            getApartmentAreaInfoRespVO.setApartmentId(roomelEntity.getApartmentId());
            return roomelEntity.getIsConcentrated();
        }).findFirst().orElse(null);
        // 该小区是否包含集中式房源
        getApartmentAreaInfoRespVO.setIsConcentrated(ObjectUtil.isNotEmpty(searchHit));

        List<GetApartmentAreaInfoRoomVO> respVOS = new ArrayList<>();
        Arrays.stream(hits).forEach(hit -> {
            RoomelEntity roomelEntity = JSONObject.parseObject(hit.getSourceAsString(), RoomelEntity.class);
            // 获取房间价格
            RoomHouse roomHouse = roomHouseRepository.myGetById(roomelEntity.getId());
            Double monthRental = roomHouse.getMonthRental();
            if (!roomHouse.getIsWhole()) {
                // 如果是合租， 获取子房间的最低价格
                List<RoomHouse> roomHouses = roomHouseRepository.getBySuperId(roomHouse.getId()).getCheckedData();
                // 过滤出租金不为空的
                roomHouses = roomHouses.stream().filter(p -> ObjectUtil.isNotEmpty(p.getMonthRental())).collect(Collectors.toList());
                Optional<RoomHouse> reduce = roomHouses.stream().reduce((p1, p2) -> p1.getMonthRental() < p2.getMonthRental() ? p1 : p2);
                if (reduce.isPresent()) {
                    monthRental = reduce.get().getMonthRental();
                }
            }
            StringBuilder sb = new StringBuilder();
            sb.append(roomHouse.getDistrict());
            sb.append(roomHouse.getStreet());
            sb.append(roomHouse.getAddress());

            String picUrls = roomHouse.getPicUrl();
            String picUrl = "";
            if (ObjectUtil.isNotEmpty(picUrls)) {
                picUrl = picUrls.split(",")[0];
            }

            GetApartmentAreaInfoRoomVO respVO = GetApartmentAreaInfoRoomVO.builder()
                    .roomHouseId(roomHouse.getId())
                    .space(roomelEntity.getSpace())
                    .roomHouseTypeName(roomelEntity.getRoomHouseTypeName())
                    .buildingNo(roomelEntity.getBuildingNo())
                    .unitNo(roomelEntity.getUnitNo())
                    .roomHouseName(roomelEntity.getRoomHouseName())
                    .monthRental(monthRental)
                    .roomFullName(RoomHouseUtil.getHouseFullName(roomHouse))
                    .toward(ObjectUtil.isNotEmpty(roomHouse.getToward()) ? TowardEnum.getNameByValue(roomHouse.getToward()) : "")
                    .roomType(ObjectUtil.isNotEmpty(roomHouse.getRoomType()) ? RoomTypeEnum.getNameByValue(roomHouse.getRoomType()) : "")
                    .address(sb.toString())
                    .picUrl(picUrl)
                    .build();
            respVOS.add(respVO);
        });
        getApartmentAreaInfoRespVO.setEmptyNum(emptyRoomNum)
                .setTotalNum(total)
                .setPage(new PageResult<>(respVOS, total));
        return CommonResult.success(getApartmentAreaInfoRespVO);
    }

    @Override
    public List<MetroRouteRespVO> getMetroRoute() {
        LambdaQueryWrapper<RoomHFMetro> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(RoomHFMetro::getLevel, 1);
        wrapper.orderByAsc(RoomHFMetro::getSort);
        List<RoomHFMetro> list = roomHFMetroRepository.list(wrapper);
        List<MetroRouteRespVO> respVOS = new ArrayList<>();
        list.forEach(p -> {
            MetroRouteRespVO respVO = new MetroRouteRespVO();
            BeanUtil.copyProperties(p, respVO);
            respVOS.add(respVO);
        });
        return respVOS;
    }

    /**
     * 获取地铁线路下的站点
     *
     * @param id
     * @return
     */
    @Override
    public List<MetroSiteRespVO> getMetroSite(@NonNull Long id) {
        LambdaQueryWrapper<RoomHFMetro> wrapper = Wrappers.lambdaQuery();
        wrapper.eq(RoomHFMetro::getLevel, 2);
        wrapper.eq(RoomHFMetro::getPid, id);
        wrapper.orderByDesc(RoomHFMetro::getSort);
        List<RoomHFMetro> list = roomHFMetroRepository.list(wrapper);
        List<MetroSiteRespVO> respVOS = new ArrayList<>();
        list.forEach(p -> {
            MetroSiteRespVO respVO = new MetroSiteRespVO();
            BeanUtil.copyProperties(p, respVO);
            respVOS.add(respVO);
        });
        return respVOS;
    }

    /**
     * 地图找房-根据区域获取街道房源情况
     *
     * @param district
     * @param tenantId
     * @return
     */
    @Override
    public CommonResult<List<GetMapRespVO>> getStreetInfoByDistrict(String district, String street, Long tenantId) {
        List<GetMapRespVO> respVOS = new ArrayList<>();
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
                .must(QueryBuilders.matchQuery(TenantId, tenantId))
                .must(QueryBuilders.matchQuery(IsStop, false))
                .must(QueryBuilders.termQuery(District, district))
                // 只查询superId为空的房源信息
                .mustNot(QueryBuilders.existsQuery(SuperId));
        // 按街道分组
        TermsAggregationBuilder group = AggregationBuilders.terms("group").field("street.keyword");

        if (StrUtil.isNotEmpty(street)) {
            boolQueryBuilder.must(QueryBuilders.termQuery("street.keyword", street));
            group = AggregationBuilders.terms("group").field("apartmentAreaId");
            // 获取小区名
            TopHitsAggregationBuilder areaInfoGroup = AggregationBuilders.topHits("areaInfo").fetchSource(new String[]{"apartmentAreaName", "apartmentAreaGeo"}, null).size(1);
            group.subAggregation(areaInfoGroup);
        }

        // 原生查询请求
        SearchRequest searchRequest = new SearchRequest(ApiConstants.ROOMELENTIRY);
        // 查询条件构造
        SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
        // 添加查询条件
        searchSourceBuilder.query(boolQueryBuilder);
        // 添加聚合条件
        searchSourceBuilder.aggregation(group);
        // 将条件构造作为查询请求的属性
        searchRequest.source(searchSourceBuilder);
        // 进行查询
        try {
            SearchResponse search = elasticsearchTemplate.search(searchRequest);
            ParsedTerms groupTerms = (ParsedTerms) search.getAggregations().asMap().get("group");
            if (StrUtil.isNotEmpty(street)) {
                List<? extends Terms.Bucket> areaTermsBuckets = groupTerms.getBuckets();
                areaTermsBuckets.forEach(areaTerm -> {
                    // 小区内有几条数据
                    GetMapRespVO respVO = new GetMapRespVO();
                    respVO.setRoomAreaId(Long.parseLong(areaTerm.getKeyAsString()));
                    respVO.setNum(areaTerm.getDocCount());
                    ParsedTopHits areaInfo = (ParsedTopHits) areaTerm.getAggregations().getAsMap().get("areaInfo");
                    SearchHit[] hits = areaInfo.getHits().getHits();
                    for (SearchHit hit : hits) {
                        Map<String, Object> sourceAsMap = hit.getSourceAsMap();
                        String apartmentAreaName = (String) sourceAsMap.get("apartmentAreaName");
                        String geo = (String) sourceAsMap.get("apartmentAreaGeo");
                        String[] geoSplit = geo.split(",");
                        if (ObjectUtil.isNotEmpty(geoSplit)) {
                            respVO.setLatLon(String.valueOf(geoSplit[1] + "," + geoSplit[0]));
                        }
                        respVO.setName(apartmentAreaName);
                    }
                    respVOS.add(respVO);
                });
            } else {
                groupTerms.getBuckets().forEach(term -> {
                    GetMapRespVO respVO = new GetMapRespVO();
                    String name = term.getKeyAsString();
                    long count = term.getDocCount();
                    respVO.setName(name);
                    respVO.setNum(count);
                    String lngLat = this.getLngLat(respVO.getName());
                    respVO.setLatLon(lngLat);
                    respVOS.add(respVO);
                });
            }
        } catch (Exception e) {
            log.error("MapServiceImpl -> getMap异常:", e);
            throw new ServiceException(10023003, e.getMessage());
        }
        return CommonResult.success(respVOS);
    }

    /**
     * 地图找房-搜索-根据小区名获取对应的小区信息
     *
     * @param searchBox      搜索框内容
     * @param isConcentrated 是否集中式
     * @param roomHouseState 房源状态 1：可租房源 2：全部房源
     * @param pageNo         分页参数
     * @param pageSize       分页参数
     * @param tenantId       租户id
     * @return
     */
    @Override
    public CommonResult<PageResult<GetMapRespVO>> mapFindSearch(String searchBox, Boolean isConcentrated, String roomHouseState, Integer pageNo, Integer pageSize, Long tenantId) {
        return roomelEntityRepository.mapFindSearch(searchBox, isConcentrated, roomHouseState, pageNo, pageSize, tenantId);
    }
}
